package craky.componentc;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.Calendar;

import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.MenuSelectionManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.BasicPopupMenuUI;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;

import com.abigdreamer.swing.component.JCButton;
import com.abigdreamer.swing.component.UIResourceManager;
import com.abigdreamer.swing.component.UIUtil;
import com.sun.awt.AWTUtilities;

import craky.util.Util;

public class JCCalendarComboBox extends JCComboBox {
	private static final long serialVersionUID = -3823377723957390277L;
	private JCCalendarPane calendarPane;

	public JCCalendarComboBox() {
		setUI(new CCalendarComboBoxUI());
		super.setEditable(true);
		JTextComponent editor = (JTextComponent) getEditor().getEditorComponent();
		editor.setEditable(false);
		editor.setCursor(new Cursor(2));
		editor.addMouseListener(new EditorListener());
		this.calendarPane.setClosable(true);
		this.calendarPane.setCloseAction(new AbstractAction() {
			private static final long serialVersionUID = 4087888078109547665L;

			public void actionPerformed(ActionEvent e) {
				JCCalendarComboBox.this.updateSelected();
			}
		});
	}

	private void updateSelected() {
		String date = this.calendarPane.getDateString();
		Object oldDate = getSelectedItem();

		if (((date != null) && (!date.equals(oldDate))) || ((oldDate != null) && (!oldDate.equals(date)))) {
			setSelectedItem(date);
		}

		if (isPopupVisible()) {
			hidePopup();
		}
	}

	public Calendar getDate() {
		return this.calendarPane.getDate();
	}

	public void setDate(Calendar date) {
		this.calendarPane.setDate(date);
		updateSelected();
	}

	public String getDateString() {
		return this.calendarPane.getDateString();
	}

	public void setDateString(String dateString) {
		this.calendarPane.setDateString(dateString);
		updateSelected();
	}

	public JCCalendarPane getCalendarPane() {
		return this.calendarPane;
	}

	private void resetBorderAfterPopupHidden() {
		Rectangle rect = new Rectangle(getLocationOnScreen(), getSize());

		if (!rect.contains(Util.getMouseLocation())) {
			mouseOut();
		}
	}

	protected void resetShortcutKeys() {
		InputMap inputMap = getInputMap(1);

		for (KeyStroke ks : inputMap.allKeys()) {
			Object value = inputMap.get(ks);

			if ((!value.equals("selectNext")) && (!value.equals("selectNext2")) && (!value.equals("selectPrevious")) && (!value.equals("selectPrevious2")))
				continue;
			inputMap.put(ks, "");
		}
	}

	@Deprecated
	public void setEditable(boolean editable) {
	}

	private class CCalendarComboBoxUI extends CComboBoxUI {
		private MouseListener mouseListener;
		private KeyListener keyListener;

		private CCalendarComboBoxUI() {
		}

		protected JButton createArrowButton() {
			JCButton button = new JCButton();
			button.setName("CalendarComboBox.arrowButton");
			button.setImage(this.buttonImage = UIResourceManager.getImage("CalendarComboBoxButtonImage"));
			button.setDisabledImage(UIUtil.toBufferedImage(this.buttonImage, 0.5F, button));
			button.setRolloverImage(this.buttonRolloverImage = UIResourceManager.getImage("CalendarComboBoxButtonRolloverImage"));
			button.setPressedImage(UIResourceManager.getImage("CalendarComboBoxButtonPressedImage"));
			button.setImageInsets(2, 2, 0, 0);
			return button;
		}

		public void configureArrowButton() {
			super.configureArrowButton();

			if (this.arrowButton != null) {
				this.arrowButton.addMouseListener(getMouseListener());
			}
		}

		public void unconfigureArrowButton() {
			super.unconfigureArrowButton();

			if (this.arrowButton != null) {
				this.arrowButton.removeMouseListener(getMouseListener());
			}
		}

		protected ComboPopup createPopup() {
			return new CCalendarPopup(this.comboBox);
		}

		protected void installListeners() {
			super.installListeners();
			this.comboBox.getEditor().getEditorComponent().addKeyListener(getKeyListener());
		}

		protected void uninstallListeners() {
			super.uninstallListeners();
			this.comboBox.getEditor().getEditorComponent().removeKeyListener(this.keyListener);
		}

		protected void selectNextPossibleValue() {
			if (((this.comboBox instanceof JCCalendarComboBox)) && (!((JCCalendarComboBox) this.comboBox).isEditableAll())) {
				return;
			}

			changeDate(1);
		}

		protected void selectPreviousPossibleValue() {
			if (((this.comboBox instanceof JCCalendarComboBox)) && (!((JCCalendarComboBox) this.comboBox).isEditableAll())) {
				return;
			}

			changeDate(-1);
		}

		private void changeDate(int day) {
			if ((this.comboBox instanceof JCCalendarComboBox)) {
				JCCalendarComboBox calendarBox = (JCCalendarComboBox) this.comboBox;
				Calendar date = calendarBox.getDate();

				if (date == null) {
					date = Calendar.getInstance();
				} else {
					date.add(6, day);
				}

				calendarBox.setDate(date);
			}
		}

		private MouseListener getMouseListener() {
			if (this.mouseListener == null) {
				this.mouseListener = new MouseAdapter() {
					private boolean visible;

					public void mouseReleased(MouseEvent e) {
						if ((this.visible) || (!(JCCalendarComboBox.CCalendarComboBoxUI.this.comboBox instanceof JCComboBox))
								|| ((JCCalendarComboBox.CCalendarComboBoxUI.this.comboBox.isEnabled()) && (((JCComboBox) JCCalendarComboBox.CCalendarComboBoxUI.this.comboBox).isEditableAll()))) {
							JCCalendarComboBox.CCalendarComboBoxUI.this.comboBox.setPopupVisible(!this.visible);
						}
					}

					public void mousePressed(MouseEvent e) {
						this.visible = JCCalendarComboBox.CCalendarComboBoxUI.this.comboBox.isPopupVisible();
						MenuSelectionManager.defaultManager().clearSelectedPath();
					}
				};
			}
			return this.mouseListener;
		}

		private KeyListener getKeyListener() {
			if (this.keyListener == null) {
				this.keyListener = new KeyAdapter() {
					public void keyPressed(KeyEvent e) {
						int keyCode = e.getKeyCode();

						if (e.getModifiers() == 0) {
							if (keyCode == 38) {
								JCCalendarComboBox.CCalendarComboBoxUI.this.selectPreviousPossibleValue();
							} else if (keyCode == 40) {
								JCCalendarComboBox.CCalendarComboBoxUI.this.selectNextPossibleValue();
							}
						}
					}
				};
			}
			return this.keyListener;
		}
	}

	private class CCalendarPopup extends BasicComboPopup {
		private static final long serialVersionUID = -2699306734874068484L;
		private final int SCREEN_HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().height;
		private JWindow heavyWeightWindow;
		private boolean buffered;

		public CCalendarPopup(JComboBox comboBox) {
			super(comboBox);
			setUI(new BasicPopupMenuUI() {
				public void installDefaults() {
				}

				protected void uninstallDefaults() {
				}
			});
			setBorder(new EmptyBorder(0, 0, 0, 0));
			setOpaque(false);
			remove(this.scroller);
			setLayout(new BorderLayout());
			add(JCCalendarComboBox.this.calendarPane = new JCCalendarPane(), "Center");
		}

		public void paint(Graphics g) {
			if (!this.buffered) {
				super.paint(g);
			} else {
				Insets insets = getInsets();
				int x = insets.left;
				int y = insets.top;
				int width = getWidth();
				int height = getHeight();
				int contentWidth = width - insets.left - insets.right;
				int contentHeight = height - insets.top - insets.bottom;
				BufferedImage image = UIUtil.getGraphicsConfiguration(this).createCompatibleImage(width, height, 3);
				BufferedImage contentImage = UIUtil.getGraphicsConfiguration(this).createCompatibleImage(contentWidth, contentHeight, 1);
				Graphics2D g2d = image.createGraphics();
				Graphics2D contentG2d = contentImage.createGraphics();
				contentG2d.translate(-x, -y);
				super.paint(g2d);
				super.paint(contentG2d);
				g2d.dispose();
				contentG2d.dispose();
				g.drawImage(image, 0, 0, this);
				g.drawImage(contentImage, x, y, this);
			}
		}

		public void setVisible(boolean visible) {
			if (visible == isVisible()) {
				return;
			}

			if (visible) {
				Object value = JCCalendarComboBox.this.getSelectedItem();
				JCCalendarComboBox.this.calendarPane.setDateString(value == null ? null : value.toString());
			} else {
				JCCalendarComboBox.this.calendarPane.clearMouseStatus();
			}

			super.setVisible(visible);

			if (UIUtil.isTranslucencySupported()) {
				if ((visible) && (!isOpaque()) && (this.heavyWeightWindow == null)) {
					this.heavyWeightWindow = getHeavyWeightWindow();
				}

				if (this.heavyWeightWindow != null) {
					this.buffered = true;
					AWTUtilities.setWindowOpaque(this.heavyWeightWindow, !visible);
				}
			}

			if (!visible) {
				JCCalendarComboBox.this.resetBorderAfterPopupHidden();
			}
		}

		public void show() {
			if (((this.comboBox instanceof JCComboBox)) && (!((JCComboBox) this.comboBox).isEditableAll())) {
				return;
			}

			super.show();
		}

		public void show(Component invoker, int x, int y) {
			if (invoker != null) {
				int parentWidth = invoker.getWidth();
				Dimension size = getPreferredSize();
				int width = size.width;
				int height = size.height;
				x = parentWidth > width ? parentWidth - width : 0;
				y = this.SCREEN_HEIGHT - (invoker.getLocationOnScreen().y + invoker.getHeight()) < height ? -height : invoker.getHeight();
			}

			super.show(invoker, x, y);
		}

		private JWindow getHeavyWeightWindow() {
			Container c = getParent();

			while ((c != null) && (!(c instanceof Window))) {
				c = c.getParent();
			}

			if ((c != null) && (c.getClass().getName().equalsIgnoreCase("javax.swing.Popup$HeavyWeightWindow"))) {
				return (JWindow) c;
			}

			return null;
		}

		@Deprecated
		public void updateUI() {
			setUI(getUI());
		}

		protected void togglePopup() {
		}
	}

	private class EditorListener extends MouseAdapter {
		private EditorListener() {
		}

		public void mousePressed(MouseEvent e) {
			JCCalendarComboBox.this.calendarPane.close();
		}
	}
}